<?php
namespace Tyrone\Lib;

class BaseRedisPool
{
    protected $redisPool;
    protected $prefix = 'redis_com_lock_';

    public function __construct($redisPool, $params = [])
    {
        $this->redisPool = $redisPool;
    }

    protected function getRedisConnection()
    {
        return $this->redisPool->get();
    }

    /**
     * 获取分布式锁（带重试）
     */
    public function lock($key, $ttl = 30, $keep = 3)
    {
        $attempts = $keep * 5; // 每 0.2s 一次，共 keep 秒
        $fullKey = $this->prefix . $key;

        for ($i = 0; $i < $attempts; $i++) {
            $redis = $this->getRedisConnection();
            try {
                // ✅ 使用原子操作 SET ... NX EX
                $lockMark = uniqid('', true);
                $result = $redis->set($fullKey, $lockMark, [
                    'nx',  // only set if not exists
                    'ex' => $ttl
                ]);

                if ($result) {
                    return $lockMark;
                }
            } catch (\Throwable $e) {
                // 记录错误，但继续重试
                echo "Lock failed (attempt {$i}):{$key}:" . $e->getMessage() . PHP_EOL;
            } finally {
                $this->redisPool->put($redis);
            }

            \Swoole\Coroutine::sleep(0.2);
        }

        echo "获取锁失败.key: {$key}" . PHP_EOL;
        return false;
    }

    /**
     * 执行带锁的操作
     */
    public function actionLock($key, callable $action)
    {
        $lockMark = $this->lock($key);
        if (!$lockMark) {
            return false;
        }

        // ✅ 提前获取解锁连接
        $redisForUnlock = $this->getRedisConnection();
        try {
            return $action();
        } finally {
            try {
                $unlocked = $this->unlock($redisForUnlock, $key, $lockMark);
                if (!$unlocked) {
                    echo "警告：锁释放失败或已被他人释放。key: {$key}" . PHP_EOL;
                }
            } catch (\Throwable $e) {
                echo "解锁异常: " . $e->getMessage() . PHP_EOL;
            } finally {
                $this->redisPool->put($redisForUnlock);
            }
        }
    }

    /**
     * 释放锁（Lua 脚本保证原子性）
     */
    public function unlock($redis, $key, $lockMark): bool
    {
        $fullKey = $this->prefix . $key;
        $lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        try {
            $result = $redis->eval($lua, [$fullKey, $lockMark], 1);
            return $result === 1;
        } catch (\Throwable $e) {
            echo "解锁 Lua 脚本执行失败: " . $e->getMessage() . PHP_EOL;
            return false;
        }
    }
}